package extractInformation

import (
	"SearchEngine/analysis/ac"
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

var (
	oneWebCode  = make(chan string, 1) // 放入从doc_raw.bin读取的一个网页的全部源代码
	OneWebIndex = make(chan int64, 1)  // 放入从doc_raw.bin读取的一个网页的编号
	Text        = make(chan string, 1) // 经过筛选后的文本信息
)

//1.读取一个单独网页源代码
//2.抽取网页文本信息，去掉 JavaScript 代码、CSS 格式以及下拉框中的内容,去掉所有 HTML 标签
func GetExtractResult() {
	// 1.打开doc_raw.bin文件，读取一个网页,获取源码、编号
	go readOneWeb()
	// 2.抽取网页文本信息
	content, ok := <-oneWebCode
	//2.1获得网页源代码
	if !ok {
		fmt.Println("doc_raw.bin文件中的网页源代码已全部处理完毕")
		// return
	}
	//2.2去掉 JavaScript 代码、CSS 格式以及下拉框中的内容
	content = extractInformation(`<script`, `</script>`, content)
	content = extractInformation(`<style`, `</style>`, content)
	content = extractInformation(`<option`, `</option>`, content)
	//2.3去掉所有 HTML 标签
	content = extractInformation(`<!--`, `-->`, content) // 删除注释
	content = extractInformation(`<`, `>`, content)
	//2.4去掉空格等
	content = strings.Replace(content, " ", "", -1)
	content = strings.Replace(content, "\n", "", -1)
	content = strings.Replace(content, "\t", "", -1)
	content = strings.Replace(content, "\r", "", -1)
	// 3.经过上面的处理之后，我们就从网页中抽取出了我们关心的文本信息
	filmName := "/home/fcou/go/src/SearchEngine/analysis/text/text.bin"
	saveTextTofile(filmName, content)

	Text <- content //将最后的文本信息写入管道，交给分词程序处理
}

// 读取一个网页,获取源码、编号，并写入通道
func readOneWeb() {
	fmt.Println("开始读取doc_raw.bin文件")
	defer func() {
		fmt.Println("终止读取doc_raw.bin文件")
	}()
	file, err := os.Open("/home/fcou/go/src/SearchEngine/collect/docRaw/doc_raw.bin")
	if err != nil {
		fmt.Println("file open error:", err)
		return
	}
	defer file.Close()
	defer close(oneWebCode)
	defer close(OneWebIndex)

	reader := bufio.NewReader(file)
	// 循环读取全部文件信息
	for {
		//1.先读取网页编号
		webIndexString, err := reader.ReadString('\t')
		if err != nil {
			fmt.Println(err)
			return
		}
		webIndexString = strings.Trim(webIndexString, "\t") //注意去除“\t”
		webIndex, err := strconv.ParseInt(webIndexString, 10, 64)
		// fmt.Printf("网页编号是：%s\n", webIndexString)
		//2.再读取网页大小
		webSizeString, err := reader.ReadString('\t')
		if err != nil {
			fmt.Println(err)
			return
		}
		webSizeString = strings.Trim(webSizeString, "\t") //注意去除“\t”
		webSize, err := strconv.ParseInt(webSizeString, 10, 64)
		// fmt.Printf("网页大小是：%s,%d\n", webSizeString, webSize)
		// 3.根据网页大小读取全部源码
		var code strings.Builder //字符串拼接效率高
		buf := make([]byte, 4096)
		for {
			if webSize-4096 <= 0 {
				buf = buf[:webSize]
			}
			n, err := reader.Read(buf) //每4KB读取一次
			if err == io.EOF {
				fmt.Println("error:", err)
				break
			}
			if err != nil {
				fmt.Println("error:", err)
				return
			}
			// fmt.Printf("网页内容是-------------------------\n：%s", string(buf[:n]))
			code.WriteString(string(buf[:n]))
			webSize = webSize - int64(n)
			if webSize <= 0 {
				// fmt.Println("已处理完毕一个网页")
				break
			}
		}
		//4.全部源码、编号写入通道
		oneWebCode <- code.String()
		OneWebIndex <- webIndex
		// fmt.Printf("网页内容是：%s\n", code.String())
		//5.读取每个网页分割符\r\n\r\n
		reader.ReadString('\r')
		reader.ReadString('\n')
		reader.ReadString('\r')
		reader.ReadString('\n')
	}
}

//根据开头，结尾标识去掉content相关中间代码
func extractInformation(startString, endString, content string) string {
	//1.构建AC自动机
	acAutoMachine := ac.NewAcAutoMachine()
	acAutoMachine.AddPattern(startString)
	acAutoMachine.AddPattern(endString)
	acAutoMachine.Build()
	//2.根据开始，结束字符串，删除其中字符串内容
	var res strings.Builder //字符串拼接效率高
	_, startIndexs := acAutoMachine.Search(content)
	lenEndString := len(endString)
	res.WriteString(string([]rune(content)[:startIndexs[0]])) //最前段字符串写入
	for i := 1; i+1 < len(startIndexs); i = i + 2 {
		endIndex := startIndexs[i]
		startIndex := startIndexs[i+1]
		res.WriteString(string([]rune(content)[endIndex+lenEndString : startIndex])) //中间可用字符串写入
	}
	res.WriteString(string([]rune(content)[startIndexs[len(startIndexs)-1]+lenEndString:])) //最后段字符串写入
	return res.String()
}

// 经过抽取后的网页文本结果，保存到文件（text.bin）
func saveTextTofile(filmName string, text string) {
	f, err := os.OpenFile(filmName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("saveTextTofile:", err)
		return
	}
	defer f.Close()
	//使用 bufio.NewWriter 写入文件
	w := bufio.NewWriter(f)
	w.WriteString(text)
	w.Flush()
}
